package org.sam.swing.table;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import org.sam.swing.table.TableModelStateEventObject.States;
import org.sam.swing.table.verification.VerificatEventListener;
import org.sam.swing.table.verification.VerificatEventObject;
import org.sam.swing.table.verification.Verification;

/**
 * 扩展的TableModule对象 最终生成类的父类 采用模版模式
 * 
 * @author sam
 *
 */
public abstract class JSTableModel<T> extends DefaultTableModel implements TableModelListener, JTableModel<T> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5129544725258528221L;

	/**
	 * 可重入锁
	 */
	protected Lock locker = new ReentrantLock();

	/**
	 * 无数据行的构造函数
	 */
	public JSTableModel() {
		super();
	}

	/**
	 * 带参数的构造函数
	 * 
	 * @param cols
	 *            当前的列集合
	 */
	public JSTableModel(JSTableColumn[] cols) {
		super(0, cols.length);
		this.setColumnIdentifiers(cols);
	}

	/**
	 * 数据检索对象
	 */
	protected volatile RetrieveModel retireveModel = RetrieveModel.RealTime;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public RetrieveModel getRetrieveModel() {
		return retireveModel;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setRetrieveModel(RetrieveModel model) {
		this.retireveModel = model;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getOrginal()
	 */
	@Override
	public abstract T getOrginal();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setOrginal(T)
	 */
	@Override
	public abstract void setOrginal(T orginal);

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getDeletes()
	 */
	@Override
	public abstract T getDeletes();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setDeletes(T)
	 */
	@Override
	public abstract void setDeletes(T deletes);

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getCreates()
	 */
	@Override
	public abstract T getCreates();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setCreates(T)
	 */
	@Override
	public abstract void setCreates(T creates);

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getModified()
	 */
	@Override
	public abstract T getModified();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setModified(T)
	 */
	@Override
	public abstract void setModified(T modified);

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#resetUpdate()
	 */
	@Override
	public abstract void resetUpdate();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#onRetrieve()
	 */
	@Override
	public abstract int onRetrieve() throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#onDelete(int)
	 */
	@Override
	public abstract boolean onDelete(int moduleRow) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#onInsert(int)
	 */
	@Override
	public abstract boolean onInsert(int moduleRow) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#onAppend()
	 */
	@Override
	public abstract boolean onAppend() throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getTableColumns()
	 */
	@Override
	@SuppressWarnings("unchecked")
	public JSTableColumn[] getTableColumns() {
		return (JSTableColumn[]) this.columnIdentifiers.toArray(new JSTableColumn[this.getColumnCount()]);
	}

	/**
	 * 快速检索模式，默认不开启 主要是retrieve的时候，不切换数据库
	 */
	private boolean quickRetrieveModel = false;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#isQuickRetrieveModel()
	 */
	@Override
	public boolean isQuickRetrieveModel() {
		return quickRetrieveModel;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setQuickRetrieveModel(boolean)
	 */
	@Override
	public void setQuickRetrieveModel(boolean quickRetrieveModel) {
		this.quickRetrieveModel = quickRetrieveModel;
	}

	/**
	 * 是否编辑状态
	 */
	private Boolean editable = true;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getEditable()
	 */
	@Override
	public Boolean getEditable() {
		return editable;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setEditable(java.lang.Boolean)
	 */
	@Override
	public void setEditable(Boolean editable) {
		if (editable != this.editable) {
			fireStateChange(States.EDITABLE, this.editable, editable);
			this.editable = editable;
		}
	}

	/**
	 * 是否在retrieve的时候加入空白行
	 */
	private boolean retrieveWithEmptyRow = false;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#isRetrieveWithEmptyRow()
	 */
	@Override
	public boolean isRetrieveWithEmptyRow() {
		return retrieveWithEmptyRow;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setRetrieveWithEmptyRow(boolean)
	 */
	@Override
	public void setRetrieveWithEmptyRow(boolean retrieveWithEmptyRow) {
		this.retrieveWithEmptyRow = retrieveWithEmptyRow;
	}

	/**
	 * 保护起来的单元格 存储格式为，行，列
	 */
	private Map<Integer, Set<Integer>> protectCell = new LinkedHashMap<Integer, Set<Integer>>();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getProtectCell()
	 */
	@Override
	public Map<Integer, Set<Integer>> getProtectCell() {
		return protectCell;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#setProtectCell(java.util.Map)
	 */
	@Override
	public void setProtectCell(Map<Integer, Set<Integer>> protectCell) {
		this.protectCell = protectCell;
	}

	/**
	 * 注入的tablemodel对象
	 */
	private JSTableModelLinster<T> tableModelLinster;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getTableModelLinster()
	 */
	@Override
	public JSTableModelLinster<T> getTableModelLinster() {
		return tableModelLinster;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.sam.swing.table.JTableModel#setTableModelLinster(org.sam.swing.table.
	 * JSTableModelLinster)
	 */
	@Override
	public void setTableModelLinster(JSTableModelLinster<T> tableModelLinster) {
		this.tableModelLinster = tableModelLinster;
	}

	/**
	 * tablemodel操作变化的事件
	 */
	private List<TableModelStateChangeListener> stateChangListeners = new LinkedList<>();

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.sam.swing.table.JTableModel#addStateChangeListener(org.sam.swing.
	 * table.TableModelStateChangeListener)
	 */
	@Override
	public synchronized void addStateChangeListener(TableModelStateChangeListener l) {
		if (l != null && !stateChangListeners.contains(l)) {
			stateChangListeners.add(l);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.sam.swing.table.JTableModel#removeStateChangeListener(org.sam.swing.
	 * table.TableModelStateChangeListener)
	 */
	@Override
	public synchronized void removeStateChangeListener(TableModelStateChangeListener l) {
		if (l != null && stateChangListeners.contains(l)) {
			stateChangListeners.remove(l);
		}
	}

	/**
	 * 触发tablemodel变化操作
	 * 
	 * @param oldValue
	 *            原值
	 * @param newValue
	 *            新值
	 */
	public void fireStateChange(States state, Object oldValue, Object newValue) {
		TableModelStateEventObject e = new TableModelStateEventObject(this);
		e.setPreValue(oldValue);
		e.setAfterValue(newValue);
		e.setState(state);
		for (TableModelStateChangeListener l : stateChangListeners) {
			if (l != null) {
				l.stateChanged(e);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#isCellEditable(int, int)
	 */
	@Override
	public boolean isCellEditable(int row, int column) {

		if (!this.getEditable())
			return false;

		if (protectCell != null && protectCell.containsKey(row)) {
			if (protectCell.get(row).contains(column))
				return false;
		}

		if (this.columnIdentifiers != null && this.columnIdentifiers.size() > column) {
			if (this.columnIdentifiers.get(column) instanceof JSTableColumn)
				return ((JSTableColumn) this.columnIdentifiers.get(column)).isEditable();
		}

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#hasChange()
	 */
	@Override
	public abstract boolean hasChange();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#moveRow(int, int)
	 */
	@Override
	public abstract void moveRow(int srcRow, int tarRow) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getDatas()
	 */
	@Override
	public abstract T getDatas() throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#reBuildIndex(int, int, int)
	 */
	@Override
	public abstract void reBuildIndex(int colIndex, int begin, int seed) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getData(int)
	 */
	@Override
	public abstract Object getData(int modelRow) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getCellData(int)
	 */
	@Override
	public Object[] getCellData(int modelRow) throws Exception {

		int iOriginal = this.findColumn(JSTableColumn.COLUMN_ORIGINAL);
		if (iOriginal < 0)
			throw new Exception("Not include original data");

		if (modelRow < 0 || modelRow >= this.getRowCount())
			throw new Exception("modelRow over index");

		Object[] datas = new Object[this.getColumnCount()];

		for (int i = 0; i < this.getColumnCount(); i++) {
			datas[i] = this.getValueAt(modelRow, i);
		}

		return datas;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#createNew()
	 */
	@Override
	public abstract Object[] createNew() throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#findIndexOf(java.lang.Object)
	 */
	@Override
	public int findIndexOf(Object data) throws Exception {
		int rowCount = this.getRowCount();
		for (int i = 0; i < rowCount; i++) {
			if (this.getData(i) == data)
				return i;
		}

		return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#clear()
	 */
	@Override
	public abstract void clear() throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#insert(int, java.lang.Object)
	 */
	@Override
	public abstract void insert(int row, Object t) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#replace(int, java.lang.Object)
	 */
	@Override
	public abstract void replace(int row, Object t) throws Exception;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getColumnClass(int)
	 */
	@Override
	public Class<?> getColumnClass(int columnIndex) {
		JSTableColumn[] tableColumns = this.getTableColumns();
		if (columnIndex < 0 || columnIndex > tableColumns.length) {
			// 因为底层是直接返回的Object.class
			return super.getColumnClass(columnIndex);
		}
		Class<?> result = tableColumns[columnIndex].getColCls();
		if (result != null)
			return result;
		
		Object defaultValue = tableColumns[columnIndex].getDefaultValue();
		if (defaultValue != null) {
			return defaultValue.getClass();
		}

		return Object.class;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#removeAll()
	 */
	@Override
	public void removeAll() throws Exception {
		for (int i = this.getRowCount() - 1; i >= 0; i--) {
			this.removeRow(i);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#updateValueAt(java.lang.Object, int,
	 * int)
	 */
	@Override
	public void updateValueAt(Object aValue, int row, int column) throws Exception {
		this.removeTableModelListener(this);
		try {
			this.setValueAt(aValue, row, column);
		} finally {
			this.addTableModelListener(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#getColData(int, int, int)
	 */
	@Override
	public Object[] getColData(int colIndex, int beginRow, int endRow) throws Exception {

		if (colIndex < 0 || colIndex >= this.getColumnCount())
			throw new Exception("column over index");

		if (beginRow < 0 || beginRow >= this.getRowCount())
			throw new Exception("beginRow over index");

		if (endRow < 0 || endRow >= this.getRowCount())
			throw new Exception("endRow over index");

		if (beginRow > endRow)
			throw new Exception("beginRow more than endRow");

		Object[] result = new Object[endRow - beginRow + 1];
		for (int i = beginRow; i <= endRow; i++) {
			result[i] = this.getValueAt(i, colIndex);
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#update()
	 */
	@Override
	public boolean update() throws Exception {

		//加入校验代码，直接截断操作
		if (!this.verificatData()){
			return false;
		}

		JSTableModelEvent event = new JSTableModelEvent(this);

		this.getTableModelLinster().beforeUpdate(event);
		if (event.isCancel() || !event.getResult())
			return false;

		if (!this.getTableModelLinster().update(event))
			return false;

		event.setCancel(false);
		event.setResult(true);
		this.getTableModelLinster().afterUpdate(event);

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#retrieve()
	 */
	@Override
	public int retrieve() throws Exception {

		this.removeTableModelListener(this);

		try {
			JSTableModelEvent event = new JSTableModelEvent(this);

			this.getTableModelLinster().beforeRetrieve(event);
			if (event.isCancel() || !event.getResult())
				return -1;

			int iResult = onRetrieve();

			event.setCancel(false);
			event.setRow(iResult);
			event.setResult(iResult >= 0);
			this.getTableModelLinster().afterRetrieve(event);

			return iResult;

		} finally {
			this.addTableModelListener(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#delete(int)
	 */
	@Override
	public boolean delete(int modelRow) throws Exception {

		JSTableModelEvent event = new JSTableModelEvent(this);
		event.setRow(modelRow);

		this.getTableModelLinster().beforeDelete(event);
		if (event.isCancel() || !event.getResult())
			return false;

		if (!this.onDelete(modelRow))
			return false;

		event.setCancel(false);
		event.setResult(true);
		this.getTableModelLinster().afterDelete(event);

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#append()
	 */
	@Override
	public boolean append() throws Exception {

		JSTableModelEvent event = new JSTableModelEvent(this);

		this.getTableModelLinster().beforeAppend(event);
		if (event.isCancel() || !event.getResult())
			return false;

		if (!this.onAppend())
			return false;

		event.setCancel(false);
		event.setResult(true);
		event.setRow(this.getRowCount() - 1);
		this.getTableModelLinster().afterAppend(event);

		return true;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.sam.swing.table.JTableModel#insert(int)
	 */
	@Override
	public boolean insert(int modelRow) throws Exception {

		JSTableModelEvent event = new JSTableModelEvent(this);
		event.setRow(modelRow);

		this.getTableModelLinster().beforeInsert(event);
		if (event.isCancel() || !event.getResult())
			return false;

		if (!this.onInsert(modelRow))
			return false;

		event.setCancel(false);
		event.setResult(true);
		this.getTableModelLinster().afterInsert(event);

		return true;
	}

	// begin 以下是校验相关的代码

	/**
	 * 校验结果事件列表
	 */
	protected List<VerificatEventListener> verificatEventListeneries = Collections.synchronizedList(new LinkedList<>());

	/**
	 * 校验数据的操作 执行顺序为校验对象；列校验器，实体校验器(adapter)
	 * 
	 * @return
	 */
	protected boolean verificatData() {
		// 校验事件对象
		VerificatEventObject eventObject = new VerificatEventObject(this);
		int colCount = this.getColumnCount();

		// 对象验证
		if (this.getVerification() != null) {
			for (int row = 0, count = this.getRowCount(); row < count; row++) {
				for (int column = 0; column < colCount; column++) {
					if (!this.getVerification().check(this, row, column)) {
						eventObject.setVerificatResult(false);
						eventObject.addResult(row, this.getVerification().getMsg(this, row, column));
					}
				}
			}
		}
		if (!eventObject.isVerificatResult()) {
			for (VerificatEventListener l : this.verificatEventListeneries) {
				l.verificatResult(eventObject);
			}
			return false;
		}

		// 列校验对象
		if (this.isTableColumnVerificatior()) {
			JSTableColumn[] columns = this.getTableColumns();
			for (int row = 0, count = this.getRowCount(); row < count; row++) {
				for (JSTableColumn col : columns) {
					if (col.getVerficator() != null && !col.getVerficator().check(this, row, col.getModelIndex())) {
						eventObject.setVerificatResult(false);
						eventObject.addResult(row, col.getVerficator().getMsg(this, row, col.getModelIndex()));
					}
				}
			}
		}
		if (!eventObject.isVerificatResult()) {
			for (VerificatEventListener l : this.verificatEventListeneries) {
				l.verificatResult(eventObject);
			}
			return false;
		}

		// 客户端校验
		if (this.isUseClientVerification()) {
			if (!this.getTableModelLinster().check(this,eventObject.getResult())) {
				eventObject.setVerificatResult(false);
			}
		}
		if (!eventObject.isVerificatResult()) {
			for (VerificatEventListener l : this.verificatEventListeneries) {
				l.verificatResult(eventObject);
			}
			return false;
		}

		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addVerificatEventListener(VerificatEventListener listener) {
		if (!verificatEventListeneries.contains(listener)) {
			this.verificatEventListeneries.add(listener);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeVerificatEventListener(VerificatEventListener listener) {
		if (verificatEventListeneries.contains(listener)) {
			this.verificatEventListeneries.remove(listener);
		}
	}
	
	/**
	 * 是否使用客户端校验;
	 * 在ModelAdapter里客户端里由重写的地方校验;
	 * 这里仅做标识,不做处理
	 */
	private boolean useClientVerification = false;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isUseClientVerification() {
		return useClientVerification;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setUseClientVerification(boolean used) {
		this.useClientVerification = used;
	}
	
	/**
	 * 验证器对象
	 */
	private Verification verification;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Verification getVerification() {
		return verification;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setVerification(Verification verification) {
		this.verification = verification;
	}
	
	/**
	 * 是否开启列对象校验;(设置列上的vertifactor对象)
	 * 在ModelAdapter里客户端里由重写的地方校验;
	 */
	private boolean tableColumnVerificatior = false;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isTableColumnVerificatior() {
		return tableColumnVerificatior;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setTableColumnVerificatior(boolean verificat) {
		this.tableColumnVerificatior = verificat;
	}
}
